﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RPGXEngine.IO;
using RPGXEngine.Factories;
using System.IO;
using System.Diagnostics;
using System.Xml.Linq;
using System.Xml;
using RPGXEngine.Object;
using Microsoft.Xna.Framework.Graphics;
using RPGXEngine.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Input;

using DotNetX;

namespace RPGXEngine.World
{
    public class GameWorld
    {
        #region Properties

        public Version CurrentVersion
        {
            get
            {
                return new Version(0, 1, 0, 0);
            }
        }

        public string FileExtension
        {
            get
            {
                return "wld";
            }
        }

        public ITextFileLoader TextFileLoader
        {
            get { return _textFileLoader; }
            set { _textFileLoader = value; }
        }
        private ITextFileLoader _textFileLoader = null;

        public Version Version
        {
            get { return _version; }
            set { _version = value; }
        }
        private Version _version = null;

        public List<GameMesh> Meshes
        {
            get { return _meshes; }
            set { _meshes = value; }
        }
        private List<GameMesh> _meshes = new List<GameMesh>();

        public Camera Camera
        {
            get { return _camera; }
            set { _camera = value; }
        }
        private Camera _camera = new Camera();

        public Effect DefaultEffect
        {
            get { return _defaultEffect; }
            set { _defaultEffect = value; }
        }
        private Effect _defaultEffect = null;

        public ContentManager Content
        {
            get { return _content; }
            set { _content = value; }
        }
        private ContentManager _content = null;

        public GraphicsDevice GraphicsDevice
        {
            get { return _graphicsDevice; }
            set { _graphicsDevice = value; }
        }
        private GraphicsDevice _graphicsDevice = null;

        public RenderTarget2D FinalSceneRenderTarget
        {
            get { return _finalSceneRenderTarget; }
            set { _finalSceneRenderTarget = value; }
        }
        private RenderTarget2D _finalSceneRenderTarget = null;

        public int Width
        {
            get { return _width; }
            set { _width = value; }
        }
        private int _width = 0;

        public int Height
        {
            get { return _height; }
            set { _height = value; }
        }
        private int _height = 0;

        public bool IsLoaded
        {
            get { return _isLoaded; }
            set { _isLoaded = value; }
        }
        private bool _isLoaded = false;

        #endregion

        #region Public Methods

        public GameWorld()
        {
            this.TextFileLoader = TextFileLoaderFactory.Get();
        }

        public bool Load(string fileName, ContentManager content, GraphicsDevice graphicsDevice)
        {
            bool res = false;

            this.Content = content;
            this.GraphicsDevice = graphicsDevice;

            this.DefaultEffect = this.Content.Load<Effect>("Effects/Default");

            res = this.ProcessFile(fileName);

            this.FinalSceneRenderTarget = new RenderTarget2D(this.GraphicsDevice, this.GraphicsDevice.Viewport.Width, this.GraphicsDevice.Viewport.Height,
                false, SurfaceFormat.Rgba64, DepthFormat.Depth24);

            this.Camera.Position = new Vector3(10.0f, 50.0f, 10.0f);
            this.Camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
            this.Camera.Update(this.GraphicsDevice);

            if (res)
                this.IsLoaded = true;

            return res;
        }

        public Texture2D Render()
        {
            if (!this.IsLoaded)
                return null;

            this.GraphicsDevice.SetRenderTarget(this.FinalSceneRenderTarget);

            this.RenderScene(this.Camera, "Textured");

            Texture2D finalImage = (Texture2D)this.FinalSceneRenderTarget;

            this.GraphicsDevice.SetRenderTarget(null);

            return finalImage;
        }

        public void Update(GameTime gameTime)
        {
            if (!this.IsLoaded)
                return;
        }

        #endregion

        #region Protected Methods

        protected void RenderScene(Camera camera, string technique)
        {
            int i = 0;

            Vector2 currentPosition = new Vector2(camera.Position.X, camera.Position.Z);
            currentPosition.X = currentPosition.X / this.Width;
            currentPosition.Y = currentPosition.Y / this.Height;

            foreach (GameMesh mesh in this.Meshes)
            {
                int x = i / this.Width;
                int y = i % this.Width;
                i++;

                Vector2 tilePosition = new Vector2(x, y);
                Vector2 distance = tilePosition - currentPosition;
                float length = Math.Abs(distance.Length());

                // get the tiles all around the current one
                if (length >= 2.0f)
                    continue;

                mesh.Render(Matrix.Identity, this.Camera, "Textured");
            }
        }

        protected virtual bool ProcessFile(string fileName)
        {
            bool res = false;

            try
            {
                if (this.ValidateFileName(fileName))
                {
                    string fileContents = this.TextFileLoader.Load(fileName);

                    XDocument doc = XDocument.Parse(fileContents);

                    if (this.LoadVersion(doc))
                    {
                        if (this.LoadMeshes(doc))
                        {
                            res = true;
                        }
                    }
                }
            }
            catch (FileNotFoundException ex)
            {
                Trace.TraceError(ex.Message);
            }
            catch (XmlException ex)
            {
                Trace.TraceError(ex.Message);
            }

            return res;
        }

        protected virtual bool ValidateFileName(string fileName)
        {
            bool res = false;

            fileName = fileName.ToLower();

            if (fileName.EndsWith(this.FileExtension))
                res = true;

            return res;
        }

        protected virtual bool LoadVersion(XDocument doc)
        {
            bool res = false;

            string version = doc.Element("world").Element("version").Value;

            try
            {
                this.Version = new Version(version);

                if (this.Version <= this.CurrentVersion)
                {
                    res = true;
                }
            }
            catch (ArgumentException ex)
            {
                Trace.TraceError(ex.Message);
            }

            return res;
        }

        protected bool LoadMeshes(XDocument doc)
        {
            bool res = false;

            this.Width = doc.Element("world").Element("terrain").Attribute("width").Value.ToInt32OrZero();
            this.Height = doc.Element("world").Element("terrain").Attribute("height").Value.ToInt32OrZero();

            var meshes = doc.Element("world").Element("terrain").Elements("gamemesh");

            foreach (var mesh in meshes)
            {
                GameMesh gameMesh = new GameMesh();

                if (gameMesh.FromXML(mesh))
                {
                    this.Meshes.Add(gameMesh);

                    gameMesh.Load(this.Content, this.DefaultEffect);
                }
            }

            res = true;

            return res;
        }

        #endregion
    }
}
